package eerbaar.shared.internal.persist;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import eerbaar.shared.exceptions.GameException;
import eerbaar.shared.internal.persist.load.ObjectFactory;
import eerbaar.shared.internal.persist.save.NodeSaver;
import eerbaar.shared.persist.IPersistService;
import eerbaar.shared.persist.IPersister;
import eerbaar.shared.persist.ISaver;
import eerbaar.shared.tree.INode;

public class PersistService implements IPersistService {

    private static PersistService service = new PersistService();

    public static IPersistService service() {
        return PersistService.service;
    }

    private final Map<String, IPersister<?>> map = new HashMap<String, IPersister<?>>();

    private final Map<Class<?>, IPersister<?>> map2 = new HashMap<Class<?>, IPersister<?>>();

    private PersistService() {
        //
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> ISaver<T> getLeastDerivedClass(final ISaver<?> inPersistable) {

        if (inPersistable == null) {
            System.out.println("" + inPersistable);
        }
        assert (inPersistable != null);

        ISaver<T> result = null;

        Iterator<IPersister<?>> i = this.map.values().iterator();
        while (i.hasNext()) {

            ISaver<?> test = i.next();

            if (test.isAssignableFrom(inPersistable)) {

                if (result != null) {

                    if (result.isAssignableFrom(test)) {

                        result = (ISaver<T>) test;
                        Object obj = inPersistable.getTarget();
                        result.setTarget((T) obj);
                    }
                } else {

                    result = (ISaver<T>) test;
                    Object obj = inPersistable.getTarget();
                    result.setTarget((T) obj);
                }
            }
        }

        if (result == null) {

            result = (ISaver<T>) inPersistable;
        }

        return result;
    }

    @Override
    public <T> IPersister<T> getPersister(Class<T> clazz) {

        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(clazz.getName()
                    + " is not an interface");
        }

        @SuppressWarnings("unchecked")
        IPersister<T> obj = (IPersister<T>) this.map2.get(clazz);
        return obj;
    }

    @Override
    public <T> IPersister<T> getPersister(Class<T> type, String name) {

        if (!this.map.containsKey(name)) {
            throw new IllegalArgumentException(name + " is not registered");
        }

        @SuppressWarnings("unchecked")
        IPersister<T> obj = (IPersister<T>) this.map.get(name);
        return obj;
    }

    @Override
    public <T> T load(Class<T> type, INode node) throws GameException {

        ObjectFactory factory = new ObjectFactory();
        return factory.create(type, node);
    }

    @Override
    public <T> void register(Class<T> clazz, IPersister<T> persistable) {

        if (!clazz.isInterface()) {
            throw new IllegalArgumentException(clazz.getName()
                    + " is not an interface");
        }

        if (!this.map2.containsKey(clazz)) {
            this.map2.put(clazz, persistable);
        }

        if (this.map.containsKey(clazz.getName())) {
            return;
        }

        this.map.put(clazz.getName(), persistable);
    }

    @Override
    public <T> INode save(Class<T> type, ISaver<T> saveable) {

        NodeSaver saver = new NodeSaver(this);
        INode node = saver.createNodes(saveable);

        return node;
    }

}
